package com.lin.demo.aspacts;

import com.lin.demo.annotations.CacheLock;
import com.lin.demo.config.AppException;
import com.lin.demo.config.RedisLockHelper;
import com.lin.demo.service.CacheKeyGenerator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.UUID;

/**
 * @Author zimingl
 * @Date 2023/4/1 0:39
 * @Description: LockMethodInterceptor
 */
@Aspect
@Component
@Slf4j
public class RedisLockInterceptor {
    @Resource
    private RedisLockHelper redisLockHelper;

    @Resource
    private CacheKeyGenerator cacheKeyGenerator;

    @Around("execution(public * *(..)) && @annotation(com.lin.demo.annotations.CacheLock)")
    public Object interceptor(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        CacheLock lock = method.getAnnotation(CacheLock.class);
        if (StringUtils.isEmpty(lock.prefix())) {
            throw new RuntimeException("lock key don't null...");
        }
        final String lockKey = cacheKeyGenerator.getLockKey(pjp);
        String value = UUID.randomUUID().toString();
        try {
            // 假设上锁成功，但是设置过期时间失效，以后拿到的都是 false
            final boolean success = redisLockHelper.lock(lockKey, value, lock.expire(), lock.timeUnit());
            if (!success) {
                throw new AppException(HttpStatus.BAD_REQUEST.value(), "请勿重复提交！");
            }
            try {
                return pjp.proceed();
            } catch (Throwable throwable) {
                log.error(throwable.getMessage(), throwable);
            }
        } finally {
            redisLockHelper.unlock(lockKey, value);
        }
        return null;
    }
}
